<?php

namespace app\amqp\customer;

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Exchange\AMQPExchangeType;
use PhpAmqpLib\Message\AMQPMessage;
use think\facade\Log;

class TestCustomer
{
    public const EXCHANGE_NAME = 'test';
    public const EXCHANGE_TYPE = AMQPExchangeType::DIRECT;
    public const ROUTING_KEY = 'test_queue';
    public const QUEUE_NAME = 'test_queue';

    public function customer()
    {
        $amqpConfig = config('amqp');

        $conn = new AMQPStreamConnection(
            host: $amqpConfig['host'],
            port: $amqpConfig['port'],
            user: $amqpConfig['user'],
            password: $amqpConfig['password'],
            vhost: $amqpConfig['vhost']
        );

        $channel = $conn->channel();

        /*
         * 设置消费者（Consumer）客户端同时只处理一条队列
         * 这样是告诉RabbitMQ，再同一时刻，不要发送超过1条消息给一个消费者（Consumer），
         * 直到它已经处理了上一条消息并且作出了响应。这样，RabbitMQ就会把消息分发给下一个空闲的消费者（Consumer）。
         */
        $channel->basic_qos(0, 1, false);

        /*
         * 创建队列(Queue)
         * name: hello         // 队列名称
         * passive: false      // 如果设置true存在则返回OK，否则就报错。设置false存在返回OK，不存在则自动创建
         * durable: true       // 是否持久化，设置false是存放到内存中RabbitMQ重启后会丢失,
         *                        设置true则代表是一个持久的队列，服务重启之后也会存在，因为服务会把持久化的Queue存放在硬盘上，当服务重启的时候，会重新加载之前被持久化的Queue
         * exclusive: false    // 是否排他，指定该选项为true则队列只对当前连接有效，连接断开后自动删除
         * auto_delete: false // 是否自动删除，当最后一个消费者断开连接之后队列是否自动被删除
         */
        $channel->queue_declare(
            queue: self::QUEUE_NAME,
            passive: false,
            durable: true,
            exclusive: false,
            auto_delete: false
        );

        /*
         * 创建交换机(Exchange)
         * name: vckai_exchange// 交换机名称
         * type: direct        // 交换机类型，分别为direct/fanout/topic，参考另外文章的Exchange Type说明。
         * passive: false      // 如果设置true存在则返回OK，否则就报错。设置false存在返回OK，不存在则自动创建
         * durable: false      // 是否持久化，设置false是存放到内存中的，RabbitMQ重启后会丢失
         * auto_delete: false  // 是否自动删除，当最后一个消费者断开连接之后队列是否自动被删除
         */
        $channel->exchange_declare(
            exchange: self::EXCHANGE_NAME,
            type: self::EXCHANGE_TYPE,
            passive: false,
            durable: true,
            auto_delete: false,
        );

        // 交换机与队列绑定
        $channel->queue_bind(
            queue: self::QUEUE_NAME,
            exchange: self::EXCHANGE_NAME,
            routing_key: self::ROUTING_KEY
        );

        /*
            消费消息
            queue: 从哪里获取消息的队列
            consumer_tag: 消费者标识符,用于区分多个客户端
            no_local: 不接收此使用者发布的消息
            no_ack: 设置为true，则使用者将使用自动确认模式。详情请参见.
                        自动ACK：消息一旦被接收，消费者自动发送ACK
                        手动ACK：消息接收后，不会发送ACK，需要手动调用
            exclusive:是否排他，即这个队列只能由一个消费者消费。适用于任务不允许进行并发处理的情况下
            nowait: 不返回执行结果，但是如果排他开启的话，则必须需要等待结果的，如果两个一起开就会报错
            callback: :回调逻辑处理函数,PHP回调 array($this, 'process_message') 调用本对象的process_message方法
        */
        $channel->basic_consume(
            queue: self::QUEUE_NAME,
            no_ack: false,
            callback: [$this, 'onMessage']
        );

        register_shutdown_function([$this, 'shutdown'], $channel, $conn);

        // 阻塞队列监听事件
        $channel->consume();
    }

    public function onMessage(AMQPMessage $message)
    {
        var_dump($message->getBody());
        // 手动发送ACK
        $message->ack();
    }

    public function shutdown($channel, $connection)
    {
        $channel->close();
        $connection->close();
    }
}
